package datastructure;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
 * Created with IntelliJ IDEA.
 * Description:
 *
 * @author lzy
 * @create 2019-11-07 上午8:47
 */
public class QuickSort {
    /**
     * 双边法快速排序
     *
     * @param array
     * @param startIndex
     * @param endIndex
     */
    public static void quickSort(int[] array, int startIndex, int endIndex) {
        // 递归结束条件:startIndex>=endIndex
        if (startIndex >= endIndex) {
            return;
        }

        // 得到基准元素的位置
        int pivotIndex = partition(array, startIndex, endIndex);
        // 根据基准元素的位置,分成两部分进行递归排序
        quickSort(array, startIndex, pivotIndex - 1);
        quickSort(array, pivotIndex + 1, endIndex);
    }

    /**
     * 分治(双边循环法)
     *
     * @param arr
     * @param startIndex
     * @param endIndex
     * @return
     */
    public static int partition(int[] arr, int startIndex, int endIndex) {
        // 取第1个位置(也可以选择随机的位置)的元素作为基准元素
        int pivot = arr[startIndex];
        int left = startIndex;
        int right = endIndex;

        while (left != right) {
            // 控制right指针比较并左移
            while (left < right && arr[right] > pivot) {
                right--;
            }
            // 控制left指针比较并右移
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            // 交换left和right指针指向的元素
            if (left < right) {
                int p = arr[left];
                arr[left] = arr[right];
                arr[right] = p;
            }
        }

        // pivot和指针重合点交换
        arr[startIndex] = arr[left];
        arr[left] = pivot;

        return left;
    }

    /**
     * 单边排序分治算法
     *
     * @param array
     * @param startIndex
     * @param endIndex
     */
    public static void quickSort2(int[] array, int startIndex, int endIndex) {
        // 递归条件结束,startIndex>=endIndex
        if (startIndex >= endIndex) {
            return;
        }
        // 得到基准元素位置
        int pviotIndex = partition2(array, startIndex, endIndex);
        // 根据基准元素,分成两部分进行递归排序
        quickSort2(array, startIndex, pviotIndex - 1);
        quickSort2(array, pviotIndex + 1, endIndex);
    }

    /**
     * 分治(单边循环法)
     *
     * @param array
     * @param startIndex
     * @param endIndex
     * @return
     */
    private static int partition2(int[] array, int startIndex, int endIndex) {
        // 取第一个位置(也可以选择随机位置)的元素作为基准元素
        int pivot = array[startIndex];
        int mark = startIndex;

        for (int i = startIndex + 1; i <= endIndex; i++) {
            if (array[i] < pivot) {
                mark++;
                int p = array[mark];
                array[mark] = array[i];
                array[i] = p;
            }
        }

        array[startIndex] = array[mark];
        array[mark] = pivot;
        return mark;
    }

    /**
     * 使用非递归的方式实现单边快速排序
     *
     * @param arr
     * @param startIndex
     * @param endIndex
     */
    public static void quickSort3(int[] arr, int startIndex, int endIndex) {
        // 用一个集合栈来代替递归的函数栈
        Stack<Map<String, Integer>> quickSortStack = new Stack<>();
        // 整个数列
        Map<String, Integer> rootParm = new HashMap<>();
        rootParm.put("startIndex", startIndex);
        rootParm.put("endIndex", endIndex);
        quickSortStack.push(rootParm);

        //　循环结束条件，栈为空时
        while (!quickSortStack.isEmpty()) {
            // 栈顶元素出栈，得到起止下标
            Map<String, Integer> param = quickSortStack.pop();
            // 得到基准元素位置
            int pivotIndex = partition2(arr, param.get("startIndex"), param.get("endIndex"));
            // 根据基准元素分为两部分
            if (param.get("startIndex") < pivotIndex - 1) {
                Map<String, Integer> leftParam = new HashMap<>();
                leftParam.put("startIndex", param.get("startIndex"));
                leftParam.put("endIndex", pivotIndex - 1);
                quickSortStack.push(leftParam);
            }
            if (pivotIndex + 1 < param.get("endIndex")) {
                Map<String, Integer> rightParam = new HashMap<>();
                rightParam.put("startIndex", pivotIndex + 1);
                rightParam.put("endIndex", param.get("endIndex"));
                quickSortStack.push(rightParam);
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {4, 4, 6, 5, 3, 2, 8, 1};
        quickSort3(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }
}
